package fcnet

import (
	"errors"
	"io"
	"net"
	"sync"

	"gitee.com/fcsvr/fancy/fclog"
	"gitee.com/fcsvr/fancy/utils"
	"github.com/vmihailenco/msgpack/v5"
)

type Conn struct {
	TCPConn  *net.TCPConn
	ConnID   uint32
	ExitChan chan bool

	isClosed    bool
	msgChan     chan []byte
	msgBuffChan chan []byte
	closeLock   sync.Mutex

	property     map[string]interface{}
	propertyLock sync.RWMutex
}

func NewConn(conn *net.TCPConn, connID uint32) *Conn {
	c := &Conn{
		TCPConn:     conn,
		ConnID:      connID,
		ExitChan:    make(chan bool),
		isClosed:    false,
		msgChan:     make(chan []byte),
		msgBuffChan: make(chan []byte, utils.G_Data.MaxChanBuffLen),
		property:    make(map[string]interface{}),
	}

	FC_Server.GetConnMgr().Add(c)
	return c
}

func (this *Conn) StartReader() {
	fclog.Info("Conn StartReader: connid = %d, addr = %s", this.ConnID, this.RemoteAddr().String())
	defer fclog.Info("Conn StartReader: defer, connid = %d, addr = %s", this.ConnID, this.RemoteAddr().String())
	defer this.Stop()

	for {
		pack := NewPack()

		headData := make([]byte, pack.GetHeadLen())
		if _, err := io.ReadFull(this.GetTCPConn(), headData); err != nil {
			fclog.Error("Conn StartReader: connid = %d, read msg head error = %s", this.ConnID, err)
			break
		}

		fclog.Info("Conn StartReader: connid = %d, read head len = %d, head = %x", this.ConnID, pack.GetHeadLen(), headData)
		msg, err := pack.Unpack(headData)
		if err != nil {
			fclog.Error("Conn StartReader: connid = %d, unpack headData error = %s", this.ConnID, err)
			break
		}
		fclog.Debug("Conn StartReader: msg = %+v", *msg)

		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(this.GetTCPConn(), data); err != nil {
				fclog.Error("Conn StartReader: connid = %d, read data error = %s", this.ConnID, err)
				break
			}
		}
		fclog.Info("Conn StartReader: connid = %d, read data = %s", this.ConnID, string(data))
		msg.SetData(data)

		req := NewReq(this, msg)

		if utils.G_Data.WorkerPoolSize > 0 {
			FC_Server.GetMsgHandle().SendMsgToTaskQueue(req)
		} else {
			go FC_Server.GetMsgHandle().DoMsgHandle(req)
		}
	}
}

func (this *Conn) StartWriter() {
	fclog.Info("Conn StartWriter: connid = %d, addr = %s", this.ConnID, this.RemoteAddr().String())
	defer fclog.Info("Conn StartWriter: defer, connid = %d, addr = %s", this.ConnID, this.RemoteAddr().String())

	for {
		select {
		case data := <-this.msgChan:
			if _, err := this.TCPConn.Write(data); err != nil {
				fclog.Error("Conn StartWriter: connid = %d, write data error = %s", this.ConnID, err)
				return
			}
		case data, ok := <-this.msgBuffChan:
			if ok {
				if _, err := this.TCPConn.Write(data); err != nil {
					fclog.Error("Conn StartWriter: connid = %d, write buff data error = %s", this.ConnID, err)
					return
				}
			} else {
				fclog.Info("Conn StartWriter: connid = %d, msgbuff chan closed", this.ConnID)
				break
			}
		case <-this.ExitChan:
			return
		}
	}
}

func (this *Conn) Start() {
	go this.StartReader()
	go this.StartWriter()
	FC_Server.CallOnConnStart(this)
	for {
		select {
		case <-this.ExitChan:
			return
		}
	}
}

func (this *Conn) Stop() {
	this.closeLock.Lock()
	defer this.closeLock.Unlock()

	if this.isClosed == true {
		return
	}
	this.isClosed = true
	close(this.ExitChan)
	FC_Server.CallOnConnStop(this)

	this.TCPConn.Close()
	FC_Server.GetConnMgr().Remove(this)
}

func (this *Conn) SvrStop() {
	this.isClosed = true
	this.TCPConn.Close()
}

func (this *Conn) SendMsg(msgid uint32, datast any) error {
	if this.isClosed == true {
		return errors.New("Conn SendMsg: connection closed")
	}

	//data, _ := json.Marshal(datast)
	data, errData := msgpack.Marshal(datast)
	fclog.Info("Conn SendMsg: msgpack marshal data = %v, err = %v", string(data), errData)

	pack := NewPack()
	msg, err := pack.Pack(NewMsg(msgid, data))
	if err != nil {
		fclog.Error("Conn SendMsg: connid = %d, msgid = %d, pack msg error", this.ConnID, msgid)
		return errors.New("Pack error msg")
	}

	select {
	case <-this.ExitChan:
		close(this.msgChan)
		return nil
	case this.msgChan <- msg:
	}
	return nil
}

func (this *Conn) SendBuffMsg(msgid uint32, data []byte) error {
	if this.isClosed == true {
		return errors.New("Conn SendBuffMsg: Conn closed")
	}

	pack := NewPack()
	msg, err := pack.Pack(NewMsg(msgid, data))
	if err != nil {
		fclog.Error("Conn SendMsg: connid = %d, msgid = %d, pack buffmsg error", this.ConnID, msgid)
		return errors.New("Pack error msg")
	}

	select {
	case <-this.ExitChan:
		close(this.msgBuffChan)
		return nil
	case this.msgBuffChan <- msg:
	}
	return nil
}

func (this *Conn) GetTCPConn() *net.TCPConn {
	return this.TCPConn
}

func (this *Conn) GetConnID() uint32 {
	return this.ConnID
}

func (this *Conn) RemoteAddr() net.Addr {
	return this.TCPConn.RemoteAddr()
}

func (this *Conn) GetProperty(key string) (interface{}, error) {
	this.propertyLock.Lock()
	defer this.propertyLock.Unlock()

	if value, ok := this.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("Conn GetProperty: no property found")
	}
}

func (this *Conn) SetProperty(key string, value interface{}) {
	this.propertyLock.Lock()
	defer this.propertyLock.Unlock()

	this.property[key] = value
}

func (this *Conn) RemoveProperty(key string) {
	this.propertyLock.Lock()
	defer this.propertyLock.Unlock()

	delete(this.property, key)
}
